import { _decorator, Component, Label, Node, renderer, Animation, Color, JsonAsset } from 'cc';
import { core, utils } from 'cfw';
import { KM_OF_LY, Res, SceneGraph, Timer } from 'const';
import { UI } from 'notifications';
import { TipLine } from 'prefabs';
import { PoetryLine } from './poetry-line';
import { constStars, cosmos, CosmosProxy, timer } from 'proxies';
import { toThousands } from 'helpers';
import { RelativeCamera } from '../../cosmos/scripts/components/relative-camera';
const { ccclass, property } = _decorator;

@ccclass('cocosmos.AstroPatroller')
export class AstroPatroller extends Component {

    @property(Label) public velocity: Label = null;
    @property(TipLine) public tips: TipLine = null;
    @property(Node) public poetries: Node;
    @property(Animation) public animCover: Animation;
    @property(JsonAsset) public json: JsonAsset;

    protected onLoad(): void {
        this._init()._reloadTasks();
    }

    private _init(): this {
        this._rc = utils.findComp(RelativeCamera, COSMOS, MAIN);
        this._animPoetries = this.poetries.getComponent(Animation);
        this._lines = this.poetries.getComponentsInChildren(PoetryLine);
        this._lines.forEach(line => line.interval = INTERVAL);
        const mat = this.velocity.customMaterial!;
        this._vPass0 = mat.passes[0]!;
        this._handleColorStart = this._vPass0.getHandle('startColor');
        this._handleColorEnd = this._vPass0.getHandle('endColor');
        return this;
    }

    private _reloadTasks(): void {
        this._tasks.length = 0;
        // const configs = core.res.getConfigItem<PatrolTask[]>(Res.Configs.ASTRO_PATROL_TASKS, 'tasks');
        const configs = this.json.json;
        this._tasks = configs.copy();
    }

    private _patrolling = false;
    private _rotateSpeed: vec2<num.deg> = [12, 0];
    private _escapeSpeed: number = 0.1;
    private _rc: RelativeCamera;

    protected update(dt: time.sec): void {
        if (this._patrolling) {
            core.notify(UI.Zoom, -this._escapeSpeed * dt);
            this.velocity.string = this._cameraAbsoluteVelocity;
            const { pow10 } = cosmos;
            if (pow10 < MID) {
                const ratio = (pow10 - FLOOR) / SPAN01;
                Color.lerp(cStart, START0, START1, ratio);
                Color.lerp(cEnd, END0, END1, ratio);
            } else {
                const ratio = (CEIL - pow10) / SPAN12;
                Color.lerp(cStart, START1, START2, ratio);
                Color.lerp(cEnd, END1, END2, ratio);
            }
        }
    }

    protected lateUpdate(dt: time.sec): void {
        if (this._patrolling) {
            const { pow10, delta } = cosmos;
            const last = pow10 - delta;
            const [min, max] = [Math.min(last), Math.max(pow10)];
            const chosen = [];
            this._tasks.forEach(task => {
                if (task.type === 'above-switch') {
                    if (pow10 > task.pow) chosen.push(task);
                } else if (min <= task.pow && task.pow <= max) {
                    chosen.push(task);
                }
            });
            chosen.forEach(task => this._launch(task));
            this._tasks.remove(...chosen);
            this._vPass0.setUniform(this._handleColorStart, cStart);
            this._vPass0.setUniform(this._handleColorEnd, cEnd);
        }
    }

    public startPatrol(): void {
        this.unscheduleAllCallbacks();
        utils.findNode('UI Layer', 'Canvas Front')!.active = false;
        this.animCover.play('start');
        this.scheduleOnce(() => {
            this._patrolling = true;
            this.tips.string = '脱离预制：第一宇宙速度。';
            this._rc.startAutoRotating(...this._rotateSpeed);
        }, 12);
    }

    // 计算当前相对摄像机在绝对世界中的移动速度（这可能是个巨大的天文数字，返回其字符串描述）
    private get _cameraAbsoluteVelocity(): string {
        const { pow10, delta } = cosmos, { angularVelocity } = this._rc;
        const tangent = Math.parseRadian(angularVelocity);  // 切向速度
        const radial = Math.abs(delta * 10);                // 径向速度
        const arc = Math.sqrt(Math.sumSqr(tangent, radial));// 球面微分
        const dist = Math.pow(10, pow10) * arc * KM_OF_LY;  // 光年转公里
        const speed = dist.toExponential();
        const [n, e] = speed.split('e');
        const nstr = n.replace('.', ''), len = nstr.length, ne = +e;
        let res: string;
        if (ne < 3) return '0km/s';
        if (len > ne) {
            res = nstr.substring(0, ne + 1);
        } else {
            const tail = (Math.random() + '').replace('.', '');
            const lack = (ne + 1) - len;
            res = nstr + tail.substring(0, lack);
        }
        return toThousands(res) + ' km/s';
    }

    private _launch(task: PatrolTask): void {
        switch (task.type) {
            case 'poetry':
                const { lines } = task;
                let delay = 0;
                for (let i = 0; i < lines.length; i++) {
                    const line = lines[i];
                    this.scheduleOnce(() => {
                        this._lines[i].content = line;
                        this.scheduleOnce(() => this._lines[i].fade(2.5), 6.5);
                    }, delay);
                    delay = line.length * INTERVAL;
                }
                this._animPoetries.play();
                this
                break;
            case 'tips':
                const { chars } = task;
                this.tips.string = chars;
                break;
            case 'timer':
                const { scale } = task;
                timer.accelerate(scale);
                this.tips.string = '时间流逝：1秒 = ' + timer.desc + '  ';
                break;
            case 'over': this.scheduleOnce(() => this.animCover.play('over'), 10); break;
            case 'cam-esp': break;
            case 'cam-rot':
                const { powPs } = task;
                this._rc.startAutoRotating(...powPs);
                break;
            case 'above-switch': {
                switch (task.detail) {
                    // case 'names': core.notify(UI.Stars.NAMES, true); break;
                    case 'lines': core.notify(UI.Stellar.LINES, true); constStars.lineFadeIn(); break;
                    case 'cultures': core.notify(UI.Stellar.CULTURES, true); constStars.cultFadeIn(); break;
                    default: console.warn('unknown above detail', task); break;
                }
            } break;
            default: console.warn('unknown type task:', task); break;
        }
    }

    private _animPoetries: Animation;
    private _lines: PoetryLine[];
    private _tasks: PatrolTask[] = [];
    private _vPass0: renderer.Pass;
    private _handleColorStart: handle;
    private _handleColorEnd: handle;
}

const { COSMOS, Cameras: { MAIN } } = SceneGraph;

declare type PatrolTask = { pow: pow10 } & (
    { type: 'poetry', lines: string[] } |
    { type: 'over' } |
    { type: 'tips', chars: string } |
    { type: 'timer', scale: Timer } |
    { type: 'cam-esp', powPs: number } |
    { type: 'cam-rot', powPs: [az: number, ha: number, azRange?: [low: number, high: number], haRange?: [low: number, high: number]] } |
    { type: 'above-switch', detail: 'names' | 'lines' | 'cultures' }
);

const [FLOOR, CEIL] = CosmosProxy.COSMOS_POWS;
const MID = 6;
const INTERVAL = 0.15;

const SPAN01 = MID - FLOOR;
const SPAN12 = CEIL - MID;

const START0 = Color.WHITE.clone();
const START1 = Color.RED.clone();
const START2 = Color.MAGENTA.clone();
const END0 = Color.GRAY.clone();
const END1 = Color.WHITE.clone();
const END2 = new Color(255, 127, 127, 255);

const cStart = new Color;
const cEnd = new Color;